package com.internetCafes.spms.web.certificate.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.internetCafes.spms.common.CommonConstants;
import com.internetCafes.spms.common.utils.ShiroUtils;
import com.internetCafes.spms.core.exception.RRException;
import com.internetCafes.spms.core.exception.RRExceptionCodeEnum;
import com.internetCafes.spms.core.generic.GenericDao;
import com.internetCafes.spms.core.generic.GenericServiceImpl;
import com.internetCafes.spms.core.model.pageModel.DataGrid;
import com.internetCafes.spms.core.utils.R;
import com.internetCafes.spms.web.certificate.common.cont.CertTransactionConst;
import com.internetCafes.spms.web.certificate.dao.CertiTransactionInfoMapper;
import com.internetCafes.spms.web.certificate.model.dto.transaction.BorrowOutReturnBatchDTO;
import com.internetCafes.spms.web.certificate.model.dto.transaction.TransactionReturnDTO;
import com.internetCafes.spms.web.certificate.model.vo.transaction.TransactionOutListVO;
import com.internetCafes.spms.web.certificate.entity.CertiTransactionInfo;
import com.internetCafes.spms.web.certificate.entity.CertificateInfo;
import com.internetCafes.spms.web.certificate.entity.EmployeeInfo;
import com.internetCafes.spms.web.certificate.service.CertiTransactionInfoService;
import com.internetCafes.spms.web.certificate.service.CertificateInfoService;
import com.internetCafes.spms.web.certificate.service.EmployeeInfoService;
import com.internetCafes.spms.web.certificate.vo.CertiTransactionInfoVo;
import com.internetCafes.spms.web.certificate.vo.CollectInfo;
import com.internetCafes.spms.web.customer.common.cont.CommonConst;
import com.internetCafes.spms.web.sys.model.ResourceInfo;
import com.internetCafes.spms.web.sys.model.UserInfo;
import com.internetCafes.spms.web.sys.service.ResourceInfoService;
import com.internetCafes.spms.web.tenant.model.TenantDeptInfo;
import com.internetCafes.spms.web.tenant.service.TenantDeptInfoService;
import com.sms.common.util.hutool.core.bean.BeanUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 证书借还历史 CertiTransactionInfoService 实现类
 *
 * @author Sunny
 * @email rekeeper2011@hotmail.com
 * @date 2019-10-31 18:10:59
 */
@Service("certiTransactionInfoService")
public class CertiTransactionInfoServiceImpl extends GenericServiceImpl<CertiTransactionInfo, Long> implements CertiTransactionInfoService {

    @Autowired
    private CertiTransactionInfoMapper certiTransactionInfoMapper;

    @Autowired
    private ResourceInfoService resourceInfoService;

    @Autowired
    private EmployeeInfoService employeeInfoService;

    @Autowired
    private TenantDeptInfoService tenantDeptInfoService;

    @Autowired
    @Lazy
    private CertificateInfoService certificateInfoService;

    /**
     * 押证标签 1-押证 0-不押证
     */
    private final Integer WARRANT_FLAG_Y = 1;

    /**
     * 押人标签 1-押人 0-不押人
     */
    private final Integer DETAINEE_FLAG_Y = 1;

    /**
     * 按参数查询出证书流转相关信息以及证书的idType
     *
     * @param params 筛选条件
     * @return 流转记录
     * @author Llz
     */
    @Override
    public List<CertiTransactionInfoVo> selectTransInfoAndIdtypeByParams(Map<String, Object> params) {
        return certiTransactionInfoMapper.selectTransInfoAndIdtypeByParams(params);
    }

    @Override
    public Boolean deleteByCertificateId(Long certificateId) {
        return certiTransactionInfoMapper.deleteByCertificateId(certificateId) > 0;
    }

    /**
     * 批量更新流转记录内容
     *
     * @param params 参数map
     * @author Zj
     */
    @Override
    public void updateTransactionBatch(Map<String, Object> params) {
        certiTransactionInfoMapper.updateTransactionBatch(params);
    }

    /**
     * 获取对应证书的押证借出记录
     *
     * @param certificateIds 证书id
     * @return 接触记录
     * @author Zj
     */
    @Override
    public List<CertiTransactionInfo> getWarrantTransactionRecordByCertificateIds(List<Long> certificateIds) {
        return certiTransactionInfoMapper.getWarrantTransactionRecordByCertificateIds(certificateIds);
    }

    /**
     * 批量还回证书 - 弃用
     * <p>
     * 方案是根据记录id进行还回的，证书最后状态有问题
     * #目前时间不充裕如果启用需要重新修改#2021.5.8
     * </p>
     *
     * @param dto 请求参数
     * @author Zj
     */
    @Override
    public void borrowOutReturnBatch(BorrowOutReturnBatchDTO dto) {
        if (StringUtils.isBlank(dto.getRemark())
                || ObjectUtils.isEmpty(dto.getTransactionTime())) {
            throw new RRException("参数错误");
        }
        if (CollectionUtils.isEmpty(dto.getIds())) {
            return;
        }
        // 1-获取流转记录列表
        HashMap<String, Object> params = new HashMap<>();
        params.put("ids", dto.getIds());
        List<CertiTransactionInfo> transactions = query(params);

        // 2-获取对应证书列表id
        List<Long> certificateIds = transactions.stream()
                .map(CertiTransactionInfo::getCertificateId)
                .distinct()
                .collect(Collectors.toList());

        // 3-更新挂出最后状态结束时间
        params = new HashMap<>();
        params.put("ids", dto.getIds());
        params.put("transactionEndTime", dto.getTransactionTime());
        certiTransactionInfoMapper.updateTransactionBatch(params);

        // 4-更新证书状态
        params = new HashMap<>();
        params.put("ids", certificateIds);
        params.put("transactionEndTime", dto.getTransactionTime());
        certificateInfoService.updateCertificateInfoBatch(params);

        Date currentTime = new Date();
        // 5-批量新增挂回信息
        UserInfo currentUser = ShiroUtils.getUserEntity();
        List<CertiTransactionInfo> transactionSaveBatchDO = transactions.stream()
                .map(transaction ->
                        new CertiTransactionInfo(
                                currentUser.getDeptId(), transaction.getCertificateId(),
                                CommonConstants.CertiTransactionInfoType.BorrowOut_Return,
                                transaction.getToDeptId(), transaction.getToDeptName(),
                                transaction.getToUserId(), transaction.getToUserName(),
                                transaction.getToUserMobile(), transaction.getFromDeptId(),
                                transaction.getFromDeptName(), transaction.getFromUserId(),
                                transaction.getFromUserName(), transaction.getFromUserMobile(),
                                dto.getRemark(), dto.getTransactionTime(), currentUser.getId(),
                                currentTime
                        )
                )
                .collect(Collectors.toList());

        certiTransactionInfoMapper.insertBatch(transactionSaveBatchDO);
    }


    /**
     * 证书还回数据修改
     *
     * @param dto 还回数据参数
     * @author Zj
     */
    @Override
    public void transactionReturnUpdate(TransactionReturnDTO dto) {
        CertiTransactionInfo transactionInfoUpdateDO = new CertiTransactionInfo();
        BeanUtils.copyProperties(dto, transactionInfoUpdateDO);
        update(transactionInfoUpdateDO);
    }

    /**
     * 根据证书id 获取证书借出的项目列表
     *
     * @param certificateId 证书id
     * @return 证书借出项目记录
     * @author Zj
     */
    @Override
    public List<TransactionOutListVO> transactionOutProjects(Long certificateId) {
        List<TransactionOutListVO> results = new ArrayList<>();
        // 获取证书所有借出还回流转记录
        HashMap<String, Object> params = new HashMap<>();
        params.put("certificateId", certificateId);
        params.put("types", new Integer[]{
                CommonConstants.CertiTransactionInfoType.Transaction,
                CommonConstants.CertiTransactionInfoType.Transaction_Return});
        List<CertiTransactionInfo> certificateTransactions = query(params);
        long oldAmount = certificateTransactions.stream()
                .filter(transaction -> transaction.getStatusTag() == null)
                .count();
        if ((oldAmount % 2L) != 0L) {
            Optional<CertiTransactionInfo> lastTransaction = certificateTransactions.stream()
                    .filter(transaction -> transaction.getStatusTag() == null &&
                            transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction))
                    .max(Comparator.comparingLong(CertiTransactionInfo::getId));
            CertiTransactionInfo transactionInfo = lastTransaction.get();
            results.add(new TransactionOutListVO(transactionInfo.getId(), transactionInfo.getRemark()));
        } else {
            List<TransactionOutListVO> outs = certificateTransactions.stream()
                    .filter(transaction ->
                            transaction.getStatusTag().equals(CommonConstants.CertiTransactionStatusTags.IN_PROGRESS.getTag())
                                    && transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction))
                    .map(transaction -> new TransactionOutListVO(transaction.getId(), transaction.getProjectName()))
                    .collect(Collectors.toList());
            results.addAll(outs);
        }
        return results;
    }

    /**
     * 证书还回接口
     *
     * @param dto 换回参数
     * @author Zj
     */
    @Override
    public void transactionReturn(TransactionReturnDTO dto) {
        CertiTransactionInfo transactionOutInfo = findById(dto.getId());
        if (transactionOutInfo == null) {
            throw new RRException("记录不存在");
        }
        if (transactionOutInfo.getStatusTag().equals(CommonConstants.CertiTransactionStatusTags.COMPLETE.getTag())) {
            throw new RRException("借出已还回");
        }
        // 旧数据兼容 判断还回后证书状态
        boolean warrantFlag = WARRANT_FLAG_Y.equals(transactionOutInfo.getWarrantFlag())
                || ObjectUtils.isEmpty(transactionOutInfo.getStatusTag());

        Date currentTime = new Date();
        CertiTransactionInfo transactionReturnDO = new CertiTransactionInfo();
        CertificateInfo certificateInfo = certificateInfoService.findById(transactionOutInfo.getCertificateId());
        BeanUtils.copyProperties(transactionOutInfo, transactionReturnDO);
        transactionReturnDO.transactionReturnInfoCopy(
                transactionOutInfo, ShiroUtils.getUserId(), currentTime, dto.getRemark(), currentTime
        );

        /*修改原有借出记录信息*/
        transactionOutInfo.setTransactionEndTime(transactionReturnDO.getTransactionTime());
        transactionOutInfo.setStatusTag(CommonConstants.CertiTransactionStatusTags.COMPLETE.getTag());
        update(transactionOutInfo);

        certificateInfo.setKeepDeptId(transactionOutInfo.getFromDeptId());
        certificateInfo.setKeepDeptName(transactionOutInfo.getFromDeptName());
        certificateInfo.setKeepUserId(transactionOutInfo.getFromUserId());
        certificateInfo.setKeepUserName(transactionOutInfo.getFromUserName());
        certificateInfo.setKeepContactMobile(transactionOutInfo.getFromUserMobile());

        // 证书最终状态确定
        int certificateFinalStatus;
        if (warrantFlag) {
            certificateFinalStatus = CommonConstants.CertificateStatus.NO_USE;
        } else {
            HashMap<String, Object> params = new HashMap<>();
            params.put("certificateId", transactionOutInfo.getCertificateId());
            params.put("type", CommonConstants.CertiTransactionInfoType.Transaction);
            params.put("statusTag", CommonConstants.CertiTransactionStatusTags.IN_PROGRESS);
            List<CertiTransactionInfo> transactionOutInfos = query(params);
            if (CollectionUtils.isEmpty(transactionOutInfos)) {
                certificateFinalStatus = CommonConstants.CertificateStatus.NO_USE;
            } else {
                certificateFinalStatus = transactionOutInfos.stream()
                        .max(Comparator.comparing(CertiTransactionInfo::getId))
                        .get().getUsage();
            }
        }
        certificateInfo.setStatus(certificateFinalStatus);
        certificateInfoService.update(certificateInfo);

        save(transactionReturnDO);
    }

    /**
     * 根据证书id 获取证书对应人员手下所有证书流转记录
     *
     * @param params 参数
     * @return 证书流转记录
     * @author Zj
     */
    @Override
    public List<CertiTransactionInfoVo> queryEmployTransactions(Map<String, Object> params) {
        return certiTransactionInfoMapper.queryEmployTransactionsByParams(params);
    }

    /**
     * 证书借出记录修改接口
     *
     * @param certiTransactionInfo 证书信息
     * @return 记录
     * @author Zj
     */
    @Override
    public R certificateBorrowOutUpdate(CertiTransactionInfoVo certiTransactionInfo) {
        CertiTransactionInfo transactionInfoBefore = findById(certiTransactionInfo.getId());
        CertificateInfo certificateInfo = certificateInfoService.findById(transactionInfoBefore.getCertificateId());
        // 增加押证、押人修改逻辑
        //
        if (!certiTransactionInfo.getWarrantFlag().equals(transactionInfoBefore.getWarrantFlag())
                || !certiTransactionInfo.getDetaineeFlag().equals(transactionInfoBefore.getDetaineeFlag())) {
            // 之前不押人，转化为押人
            boolean orderFlag;
            /*if (!DETAINEE_FLAG_Y.equals(transactionInfoBefore.getDetaineeFlag())
                    && DETAINEE_FLAG_Y.equals(certiTransactionInfo.getDetaineeFlag())) {
                if (!detaineeAbleByCertificateVO(certiTransactionInfo)) {
                    throw new RRException("不支持押人");
                }
            }*/
            /*
             * 修改逻辑 2022-06-16
             * 如果有借出其他证书，那么就不允许再改为押证
             * 有借出记录就不能押证
             */
            Map<String, Object> params = new HashMap<>();
            params.put("employeeId", certificateInfo.getEmployeeId());
            List<CertificateInfo> employeeCertPoList = certificateInfoService.query(params);
            if (CollectionUtils.isNotEmpty(employeeCertPoList)) {
                for (CertificateInfo employeeCertPo : employeeCertPoList) {
                    if (!employeeCertPo.getId().equals(certificateInfo.getId()) && CommonConst.FlagEnum.IS.getId().equals(employeeCertPo.getBorrowOutFlag())) {
                        throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_UNABLE_DETAINEE);
                    }
                }
            }

            //
            params = new HashMap<>();
            params.put("certificateId", transactionInfoBefore.getCertificateId());
            params.put("type", new Integer[] {
                    CertTransactionConst.Type.BORROW_OUT.getId(),
                    CertTransactionConst.Type.BORROW_OUT_RETURN.getId()
            });
            // 数据修复
            List<CertiTransactionInfo> borrowOutAndReturnPoList = query(params);

            // 处理数据
            List<CertiTransactionInfo> borrowOutAndReturnRepairList = new ArrayList<>();// 需要修复的数据
            borrowOutAndReturnPoList
                    = CollectionUtil.sort(borrowOutAndReturnPoList,
                    (po1, po2) -> (int) (po2.getId() - po1.getId()));
            // 统计借出和还回的数量
            int borrowOutRecordAmount = 0;
            int borrowOutReturnRecordAmount = 0;
            for (CertiTransactionInfo certBorrowOutAndReturnPo : borrowOutAndReturnPoList) {
                if (CertTransactionConst.Type.BORROW_OUT.getId().equals(certBorrowOutAndReturnPo.getType())) {
                    borrowOutRecordAmount++;
                } else {
                    borrowOutReturnRecordAmount++;
                }
            }

            int borrowOutAmount = borrowOutRecordAmount - borrowOutReturnRecordAmount;

            List<CertiTransactionInfo> certBorrowOutAndReturnRepairList = new ArrayList<>();
            List<CertiTransactionInfo> certBorrowOutNotReturnList = new ArrayList<>();
            for (CertiTransactionInfo certBorrowOutAndReturnPo : borrowOutAndReturnPoList) {
                if (CertTransactionConst.Type.BORROW_OUT.getId().equals(certBorrowOutAndReturnPo.getType())
                        && borrowOutAmount > 0 && certBorrowOutNotReturnList.size() < borrowOutAmount) {
                    // 这里是需要还回的数据
                    certBorrowOutNotReturnList.add(certBorrowOutAndReturnPo);
                    // borrowOutNotReturnList.addAll(borrowOutAndReturnPoList.subList(0, borrowOutAmount));
                } else {
                    certBorrowOutAndReturnRepairList.add(certBorrowOutAndReturnPo);
                }
            }

            // 修复数据
            if (CollectionUtil.isNotEmpty(certBorrowOutAndReturnRepairList)) {
                List<Long> transactionRepairIdList = borrowOutAndReturnRepairList
                        .stream()
                        .filter(po -> !CertTransactionConst.StatusTag.COMPLETION.getId().equals(po.getStatusTag()))
                        .map(CertiTransactionInfo::getId)
                        .collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(transactionRepairIdList)) {
                    // 将旧的数据 完成标签修改为完成
                    params = new HashMap<>();
                    params.put("ids", transactionRepairIdList);
                    params.put("statusTag", CertTransactionConst.StatusTag.COMPLETION.getId());
                    updateTransactionBatch(params);
                }
            }
            if (CollectionUtils.isNotEmpty(certBorrowOutNotReturnList)) {
                for (CertiTransactionInfo transactionInfoPo : certBorrowOutNotReturnList) {
                    if (!transactionInfoPo.getId().equals(transactionInfoBefore.getId())) {
                        throw new RRException(RRExceptionCodeEnum.CERT_CERTIFICATE_UNABLE_WARRANT);
                    }
                }
            }

            // 之前不压证，现在押证
            /*if (!WARRANT_FLAG_Y.equals(transactionInfoBefore.getWarrantFlag())
                    && WARRANT_FLAG_Y.equals(certiTransactionInfo.getWarrantFlag())) {
                params = new HashMap<>();
                params.put("certificateId", certiTransactionInfo.getCertificateId());
                params.put("type", new Integer[]{
                        CommonConstants.CertiTransactionInfoType.Transaction,
                        CommonConstants.CertiTransactionInfoType.Transaction_Return});
                List<CertiTransactionInfo> certificateTransactions = query(params);
                if (!warrantAbleByCertificateVO(certificateTransactions, certiTransactionInfo)) {
                    throw new RRException("该证书不支持押证");
                }
            }*/
        }
        CertiTransactionInfo transactionInfo = new CertiTransactionInfo();
        BeanUtil.copyProperties(certiTransactionInfo, transactionInfo);
        if (transactionInfo.getUsage() != null && (transactionInfo.getUsage() == CommonConstants.CertificateStatus.BID_USED ||
                transactionInfo.getUsage() == CommonConstants.CertificateStatus.PROJECT_USED ||
                transactionInfo.getUsage() == CommonConstants.CertificateStatus.BID_SUCCESS ||
                transactionInfo.getUsage() == CommonConstants.CertificateStatus.OTHER_STATUS)) {
            certificateInfo.setStatus(certiTransactionInfo.getUsage());
            String remark = transactionInfo.getRemark();
            int start = remark.indexOf("[");
            int end = remark.indexOf("] ");
            if (start > end) {
                String substring = remark.substring(start + 1, end);
                String usage = "项目";
                if (certiTransactionInfo.getUsage() > 0) {
                    usage = CommonConstants.CertificateStatus.getDescr(certiTransactionInfo.getUsage());
                }
                remark = remark.replace(substring, usage);
                transactionInfo.setRemark(remark);
            }
        }
        certificateInfo.setWarrantFlag(transactionInfo.getWarrantFlag());
        if (certiTransactionInfo.getToUserId() != null) {
            EmployeeInfo e = employeeInfoService.findById(certiTransactionInfo.getToUserId());
            transactionInfo.setToUserName(e.getName());
        }
        if (certiTransactionInfo.getToDeptId() != null) {
            TenantDeptInfo d = tenantDeptInfoService.findById(certiTransactionInfo.getToDeptId());
            transactionInfo.setToDeptName(d.getDeptName());
        }
        if (certiTransactionInfo.getType() == CommonConstants.CertiTransactionInfoType.BorrowIn) {
            EmployeeInfo e = employeeInfoService.findById(certiTransactionInfo.getToUserId());
            TenantDeptInfo d = tenantDeptInfoService.findById(certiTransactionInfo.getToDeptId());
            certiTransactionInfo.setToUserName(e.getName());
            certiTransactionInfo.setToDeptName(d.getDeptName());
        }
        certiTransactionInfo.setUpdateTime(new Date());
        certiTransactionInfo.setUpdateBy(ShiroUtils.getUserId());
        update(transactionInfo);
        int update = certificateInfoService.update(certificateInfo);
        return R.ok().put("c", certificateInfo).put("ct", transactionInfoBefore);
    }

    /**
     * 检测证书是否支持继续借出
     *
     * @param certificateId 证书id
     * @return boolean
     * @author Zj
     */
    @Override
    public List<CertiTransactionInfo> checkTransactionOutList(Long certificateId) {
        Map<String, Object> params = new HashMap<>();
        params.put("certificateId", certificateId);
        params.put("type", CommonConstants.CertiTransactionInfoType.Transaction);
        /*params.put("statusTag", CommonConstants.CertiTransactionStatusTags.IN_PROGRESS.getTag());*/
        return query(params).stream()
                .filter(transactionInfo -> !CommonConstants.CertiTransactionStatusTags.COMPLETE.getTag().equals(transactionInfo.getStatusTag()))
                .collect(Collectors.toList());
    }

    /**
     * 借出信息保存前检测
     *
     * @param certiTransactionInfo 借出信息
     * @author Zj
     */
    private void checkTransactionBeforeSave(CertiTransactionInfoVo certiTransactionInfo) {
        /*
         *
         *      1-兼容历史数据，历史数据默认是为押证，但是之前数据没有借出状态标签
         *          方案：先获取历史数据，
         *          如果最后一条数据 statusFlag为空，并且为借出数据
         *          证书即为押证借出状态，不支持借出
         *          其他情况即为原来的旧数据逻辑实行已完毕
         *          如果 最后一条数据statusFlag不为空，
         *        筛选出statusFlag不为空数据，走新功能逻辑
         *      2-新功能逻辑
         *          人员证书流转记录表：
         *              判断是否选择押人
         *              如果借出 > 还回两者记录数量不同，表示还有未还回的证书 则不支持证书借出
         *                  （如果是修改，需要过滤掉对应的记录）
         *          证书流转记录表：
         *              判断是否能押证
         *              借出数量 > 还回数量
         *                  不支持借出
         */
        Map<String, Object> params = new HashMap<>();
        if (WARRANT_FLAG_Y.equals(certiTransactionInfo.getDetaineeFlag())) {
            // 人员流转记录表
            params.put("certificateId", certiTransactionInfo.getCertificateId());
            params.put("type", new Integer[]{
                    CommonConstants.CertiTransactionInfoType.Transaction,
                    CommonConstants.CertiTransactionInfoType.Transaction_Return});
            List<CertiTransactionInfoVo> employeeTransactions = queryEmployTransactions(params);
            long transactionRecordAmount = employeeTransactions
                    .stream()
                    .filter(transaction -> transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction))
                    .count();
            long transactionReturnRecordAmount = employeeTransactions
                    .stream()
                    .filter(transaction -> transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction_Return))
                    .count();
            if (transactionRecordAmount > transactionReturnRecordAmount) {

            }
        }

        /*if () {
            // 证书流转记录表
            params = new HashMap<>();
            params.put("certificateId", certiTransactionInfo.getCertificateId());
            params.put("type", new Integer[]{
                    CommonConstants.CertiTransactionInfoType.Transaction,
                    CommonConstants.CertiTransactionInfoType.Transaction_Return});
            List<CertiTransactionInfo> certificateTransactions = query(params);
        }*/
        return;
    }

    /**
     * 证书借出功能抽离
     *
     * @param certiTransactionInfo 借出传入参数
     * @author Zj
     */
    @Override
    public R certificateBorrowOut(CertiTransactionInfoVo certiTransactionInfo) {
        // 如果要押人
        if (DETAINEE_FLAG_Y.equals(certiTransactionInfo.getDetaineeFlag())) {
            if (!detaineeAbleByCertificateVO(certiTransactionInfo)) {
                throw new RRException("该证书不支持押人");
            }
        }
        Map<String, Object> params = new HashMap<>();
        params.put("certificateId", certiTransactionInfo.getCertificateId());
        params.put("types", new Integer[]{
                CommonConstants.CertiTransactionInfoType.Transaction,
                CommonConstants.CertiTransactionInfoType.Transaction_Return});
        List<CertiTransactionInfo> certificateTransactions = query(params);
        // 如果要押证
        if (WARRANT_FLAG_Y.equals(certiTransactionInfo.getWarrantFlag())) {
            if (!warrantAbleByCertificateVO(certificateTransactions, certiTransactionInfo)) {
                throw new RRException("该证书不支持押证");
            }
        }
        // 借出条件判断
        if (!transactionOutAbleByCertificateVO(certificateTransactions, certiTransactionInfo)) {
            throw new RRException("该证书不支持借出");
        }
        if (!ObjectUtils.isEmpty(certiTransactionInfo.getProjectId())) {
            // 证书借出项目去重
            params = new HashMap<>();
            params.put("certificateId", certiTransactionInfo.getCertificateId());
            params.put("type", CommonConstants.CertiTransactionInfoType.Transaction);
            params.put("projectId", certiTransactionInfo.getProjectId());
            params.put("statusTag", CommonConstants.CertiTransactionStatusTags.IN_PROGRESS.getTag());
            List<CertiTransactionInfo> confirmProjects = query(params);
            if (CollectionUtils.isNotEmpty(confirmProjects)) {
                throw new RRException("该证书已在该项目中借出");
            }
        }

        // 进行借出
        CertificateInfo certificate = certificateInfoService.findById(certiTransactionInfo.getCertificateId());
        EmployeeInfo u = employeeInfoService.findById(certificate.getEmployeeId());
        certiTransactionInfo.setTenantId(u.getTenantId());
        certiTransactionInfo.setCertificateId(certificate.getId());
        certiTransactionInfo.setCreateTime(new Date());
        certiTransactionInfo.setCreateBy(ShiroUtils.getUserId());
        if (certificate.getBorrowCandidatesSources() == null
                || certificate.getBorrowCandidatesSources() != 2) {
            certificate.setBorrowCandidatesSources(1);
            certificate.setBorrowOrgName(u.getName() + "(个人)");
        }

        certiTransactionInfo.setFromDeptId(certificate.getKeepDeptId());
        certiTransactionInfo.setFromDeptName(certificate.getKeepDeptName());
        certiTransactionInfo.setFromUserId(certificate.getKeepUserId());
        certiTransactionInfo.setFromUserName(certificate.getKeepUserName());
        certiTransactionInfo.setFromUserMobile(certificate.getKeepContactMobile());

        EmployeeInfo e = employeeInfoService.findById(certiTransactionInfo.getToUserId());
        TenantDeptInfo d = tenantDeptInfoService.findById(certiTransactionInfo.getToDeptId());
        certiTransactionInfo.setToUserName(e.getName());
        certiTransactionInfo.setToDeptName(d.getDeptName());
        String usage = "项目";
        // certiTransactionInfo.setUsage(certificate.getStatus());
        int type = CommonConstants.CertificateStatus.PROJECT_USED;
        if (certiTransactionInfo.getUsage() > 0) {
            type = certiTransactionInfo.getUsage();
            usage = CommonConstants.CertificateStatus.getDescr(certiTransactionInfo.getUsage());
        }
        certiTransactionInfo.setRemark("[" + usage + "] " + certiTransactionInfo.getRemark());
        certificate.setKeepDeptId(certiTransactionInfo.getToDeptId());
        certificate.setKeepDeptName(d.getDeptName());
        certificate.setKeepUserId(certiTransactionInfo.getToUserId());
        certificate.setKeepUserName(e.getName());
        certificate.setKeepContactMobile(certiTransactionInfo.getToUserMobile());
        certiTransactionInfo.setStatusTag(CommonConstants.CertiTransactionStatusTags.IN_PROGRESS.getTag());
        save(certiTransactionInfo);
        certificate.setStatus(type);
        certificateInfoService.update(certificate);

        params = new HashMap<>();
        params.put("certificateId", certiTransactionInfo.getCertificateId());
        List<CertiTransactionInfo> certiTransactionInfos = query(params);
        saveResource(certiTransactionInfo);
        return R.ok().put("c", certificate).put("ctList", certiTransactionInfos);
    }

    /**
     * 证书是否支持借出
     *
     * @param transactionInfoVo 证书id
     * @return t/f
     * @author Zj
     */
    @Override
    public boolean transactionOutAbleByCertificateVO(List<CertiTransactionInfo> certificateTransactions, CertiTransactionInfoVo transactionInfoVo) {
        // 1-历史前数据
        long historyTransaction = certificateTransactions.stream()
                .filter(transaction -> transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction))
                .filter(transaction -> transaction.getStatusTag() == null)
                .count();

        long historyTransactionReturn = certificateTransactions.stream()
                .filter(transaction -> transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction_Return))
                .filter(transaction -> transaction.getStatusTag() == null)
                .count();

        if (historyTransaction > historyTransactionReturn) {
            return false;
        }
        List<CertiTransactionInfo> transactionWarrant = certificateTransactions.stream()
                .filter(transaction -> CommonConstants.CertiTransactionStatusTags.IN_PROGRESS.getTag().equals(transaction.getStatusTag())
                        && transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction)
                        && WARRANT_FLAG_Y.equals(transaction.getWarrantFlag()))
                .collect(Collectors.toList());
        return !CollectionUtils.isNotEmpty(transactionWarrant);
    }

    /**
     * 证书是否支持押人判断
     *
     * @param transactionInfoVo 证书id
     * @return t/f
     * @author Zj
     */
    @Override
    public boolean detaineeAbleByCertificateVO(CertiTransactionInfoVo transactionInfoVo) {
        Map<String, Object> params = new HashMap<>();
        /*
         * 在借出数量等于返回数量的时候 说明人员证书空闲，支持押人
         */
        params.put("certificateId", transactionInfoVo.getCertificateId());
        params.put("type", new Integer[]{
                CommonConstants.CertiTransactionInfoType.Transaction,
                CommonConstants.CertiTransactionInfoType.Transaction_Return});
        List<CertiTransactionInfoVo> employeeTransactions = queryEmployTransactions(params);
        long transactionRecordAmount = employeeTransactions
                .stream()
                .filter(transaction -> !transaction.getId().equals(transactionInfoVo.getId()))
                .filter(transaction -> transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction))
                .count();
        long transactionReturnRecordAmount = employeeTransactions
                .stream()
                .filter(transaction -> !transaction.getId().equals(transactionInfoVo.getId()))
                .filter(transaction -> transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction_Return))
                .count();
        return (transactionRecordAmount <= transactionReturnRecordAmount);
    }

    /**
     * 证书是否支持押证判断
     *
     * @param transactionInfoVo 证书信息
     * @return t/f
     * @author Zj
     */
    @Override
    public boolean warrantAbleByCertificateVO(List<CertiTransactionInfo> certificateTransactions, CertiTransactionInfoVo transactionInfoVo) {
        /*
         * 在借出数量等于返回数量的时候 说明人员证书空闲，支持押证
         */
        // 证书流转记录表
        long transactionRecordAmount = certificateTransactions.stream()
                .filter(transaction -> !transaction.getId().equals(transactionInfoVo.getId()))
                .filter(transaction -> transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction))
                .count();
        long transactionReturnRecordAmount = certificateTransactions
                .stream()
                .filter(transaction -> !transaction.getId().equals(transactionInfoVo.getId()))
                .filter(transaction -> transaction.getType().equals(CommonConstants.CertiTransactionInfoType.Transaction_Return))
                .count();
        return (transactionRecordAmount <= transactionReturnRecordAmount);
    }

    @Override
    public List<CertiTransactionInfo> query(Map<String, Object> params) {
        return certiTransactionInfoMapper.selectByParams(params);
    }

    @Override
    public CertiTransactionInfo findById(Long id) {
        return certiTransactionInfoMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<CertiTransactionInfo> queryByIds(List<Long> ids) {
        Map<String, Object> params = new HashMap<>();
        params.put("ids", ids);
        return certiTransactionInfoMapper.selectByParams(params);
    }

    @Override
    public List<CertiTransactionInfo> page(Map<String, Object> params, int page, int rows) {
        params.put("page", page);
        params.put("rows", rows);
        return certiTransactionInfoMapper.selectByParams(params);
    }

    @Override
    public long count(Map<String, Object> params) {
        return certiTransactionInfoMapper.countByParams(params);
    }

    @Override
    public long countByJobParams(Map<String, Object> params) {
        return certiTransactionInfoMapper.countByJobParams(params);
    }

    @Override
    public CertiTransactionInfo findFirst(Map<String, Object> params) {
        List<CertiTransactionInfo> list = certiTransactionInfoMapper.selectByParams(params);
        CertiTransactionInfo certiTransactionInfo = null;
        if (list != null && list.size() > 0) {
            certiTransactionInfo = list.get(0);
        }
        return certiTransactionInfo;
    }

    @Override
    public int save(CertiTransactionInfo certiTransactionInfo) {
        return certiTransactionInfoMapper.insert(certiTransactionInfo);
    }

    @Override
    public void saveResource(CertiTransactionInfoVo vo) {
        Map<String, Object> params;
        if (StringUtils.isNotBlank(vo.getPayPic()) && vo.getPayPic().split(",").length > 0) {
            params = new HashMap<>();
            params.put("moduleCode", CommonConstants.ResourceInfo_PIC_Pay);
            params.put("moduleId", vo.getId());
            resourceInfoService.deleteByModuleAndUserId(params);
            String[] payPics = vo.getPayPic().split(",");
            for (String payPic : payPics) {
                ResourceInfo r = new ResourceInfo(ShiroUtils.getUserId(), CommonConstants.ResourceInfo_PIC_Pay, vo.getId(), "pic" + File.separator + payPic);
                resourceInfoService.save(r);
            }
        }
    }

    @Override
    public int saveBatch(List<CertiTransactionInfo> certiTransactionInfoList) {
        return certiTransactionInfoMapper.insertBatch(certiTransactionInfoList);
    }

    @Override
    public int saveSelective(CertiTransactionInfo certiTransactionInfo) {
        return certiTransactionInfoMapper.insertSelective(certiTransactionInfo);
    }

    @Override
    public int update(CertiTransactionInfo certiTransactionInfo) {
        return certiTransactionInfoMapper.updateByPrimaryKey(certiTransactionInfo);
    }

    @Override
    public void updateResource(CertiTransactionInfoVo vo) {
        Map<String, Object> params;
        if (StringUtils.isNotBlank(vo.getPayPic()) && vo.getPayPic().split(",").length > 0) {
            params = new HashMap<>();
            params.put("moduleCode", CommonConstants.ResourceInfo_PIC_Pay);
            params.put("moduleId", vo.getId());
            resourceInfoService.deleteByModuleAndUserId(params);
            String[] payPics = vo.getCertiPic().split(",");
            for (String payPic : payPics) {
                ResourceInfo r = new ResourceInfo(ShiroUtils.getUserId(), CommonConstants.ResourceInfo_PIC_Pay, vo.getId(), "pic" + File.separator + payPic);
                resourceInfoService.update(r);
            }
        }
    }

    @Override
    public int updateSelective(CertiTransactionInfo certiTransactionInfo) {
        return certiTransactionInfoMapper.updateByPrimaryKeySelective(certiTransactionInfo);
    }

    @Override
    public int delete(Long id) {
        return certiTransactionInfoMapper.deleteByPrimaryKey(id);
    }

    @Override
    public int deleteBatch(List<Long> ids) {
        return certiTransactionInfoMapper.deleteBatchByPrimaryKey(ids);
    }

    @Override
    public void deleteBatch(Long[] ids) {
        if (ids != null && ids.length > 0) {
            for (Long id : ids) {
                delete(id);
            }
        }
    }

    @Override
    public List<CollectInfo> CertiTransactionByJobParams(Map<String, Object> params) {
        return certiTransactionInfoMapper.CertiTransactionByJobParams(params);
    }

    @Override
    public GenericDao<CertiTransactionInfo, Long> getDao() {
        return certiTransactionInfoMapper;
    }

    public List<CertiTransactionInfoVo> selectByJobParams(Map<String, Object> params) {
        return certiTransactionInfoMapper.selectByJobParams(params);
    }

    /**
     * DataGrid
     */
    @Override
    public DataGrid dataGrid(Map<String, Object> params, int pageNo, int pageSize) {

        params.put("page", (pageNo - 1 < 0 ? -1 : pageNo - 1) * pageSize);
        params.put("rows", pageSize);
        List<CertiTransactionInfoVo> certificateInfoList = certiTransactionInfoMapper.selectByJobParams(params);
        long count = certiTransactionInfoMapper.countByJobParams(params);
        return new DataGrid(certificateInfoList, count, pageSize, pageNo);

    }

}